package system

import (
	"errors"
	"gorm.io/gorm"
	"server/global"
	comRequest "server/model/common/request"
	sysModel "server/model/system"
	sysRequest "server/model/system/request"
	"strconv"
)

const (
	MENU_GROUP_KEY_ADMIN = "admin" //后台菜单
	MENU_GROUP_KEY_FRONT = "front" //前台菜单
)

type MenuService struct {
}

var MenuServiceApp = new(MenuService)

//获取用户菜单列表
func (m *MenuService) GetUserMenuList(userId uint, groupKey string) ([]*sysModel.SysMenu, error) {
	menus, err := m.GetMenusByKey(groupKey)
	if err != nil {
		return nil, err
	}
	var filterMenus []*sysModel.SysMenu
	for _, menu := range menus {
		//未绑定权限菜单表示父级菜单，需返回
		if menu.PermId == 0 {
			filterMenus = append(filterMenus, menu)
		}
		//以保定权限菜单需验证权限
		if menu.Permission.Name != "" {
			hasPerm, err := CasbinServiceApp.Casbin().Enforce(strconv.Itoa(int(userId)), "admin", menu.Permission.Name)
			if err != nil {
				return nil, err
			}
			//验证是否有权限
			if hasPerm {
				filterMenus = append(filterMenus, menu)
			}
		}
	}
	return filterMenus, nil
}

//通过分组key获取菜单列表
func (m *MenuService) GetMenusByKey(groupKey string) ([]*sysModel.SysMenu, error) {
	group, err := m.GetMenuGroupByKey(groupKey)
	if err != nil {
		return nil, err
	}
	menus, err := m.GetMenusByGroupId(group.ID)
	if err != nil {
		return nil, err
	}
	return menus, err
}

//通过分组ID获取菜单列表
func (m *MenuService) GetMenusByGroupId(groupId uint) ([]*sysModel.SysMenu, error) {
	var menus []*sysModel.SysMenu
	err := global.GvaDb.Preload("Permission").Where(&sysModel.SysMenu{GroupId: groupId}).Order("order_num asc,id asc").Find(&menus).Error
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, errors.New("菜单无数据")
		}
		return nil, err
	}
	return menus, nil
}

// 通过搜索参数获取菜单列表
func (m *MenuService) GetMenuList(p *sysRequest.SysMenuGetList) ([]*sysModel.SysMenu, error) {
	menuList := ([]*sysModel.SysMenu)(nil)
	tx := global.GvaDb.Model(&sysModel.SysMenu{})
	if p.Name != "" {
		tx = tx.Where("menu_name like ?", "%"+p.Name+"%")
	}
	if p.GroupId != 0 {
		tx = tx.Where("group_id = ?", p.GroupId)
	}
	rs := tx.Order("order_num ASC,id ASC").Find(&menuList)
	return menuList, rs.Error
}

// 获取指定父级ID的下级菜单列表
func (m *MenuService) GetMenuChildList(parentId uint) ([]*sysModel.SysMenu, error) {
	menuList := ([]*sysModel.SysMenu)(nil)
	rs := global.GvaDb.Model(&sysModel.SysMenu{}).Where("parent_id = ? ", parentId).Order("order_num ASC,id ASC").Find(&menuList)
	return menuList, rs.Error
}

// BuildTree 构建树结构数据,(由于需要保留菜单结构数据，因此每一次树结构菜单都需要在对应的service中实现，使用公共的树方法无法还原原始菜单数据)
func (m *MenuService) BuildTree(resources []*sysModel.SysMenu) []*sysModel.SysMenu {
	//定义根树，既id=0的根节点，我用的时候并不存在于数据库
	var rootResource = sysModel.SysMenu{}
	//创建一个map，把父级相同的地址归纳起来
	DataMap := make(map[uint][]*sysModel.SysMenu, len(resources))
	//寻找对应的父级，添加子节点集合
	for key := range resources {
		pid := resources[key].ParentId
		DataMap[pid] = append(DataMap[pid], resources[key])
	}
	for key := range resources {
		if resources[key].ParentId == 0 {
			rootResource.Children = append(rootResource.Children, resources[key])
		}
		resources[key].Children = DataMap[resources[key].ID]
	}
	// 添加完成，既建立树形关系完成
	return rootResource.Children
}

// SaveMenu 保存菜单数据
func (m *MenuService) SaveMenu(d *sysModel.SysMenu) (int64, error) {
	var rs *gorm.DB
	if d.ID != 0 {
		rs = global.GvaDb.Select("*").Where("id = ?", d.ID).Updates(d)
	} else {
		rs = global.GvaDb.Create(d)
	}
	return rs.RowsAffected, rs.Error
}

// 删除菜单
func (m *MenuService) DelMenu(ids *comRequest.IdsReq) (int64, error) {
	rs := global.GvaDb.Delete(&sysModel.SysMenu{}, ids.Ids)
	return rs.RowsAffected, rs.Error
}

// 设置排序
func (m *MenuService) SetOrder(p *sysRequest.SysMenuSetOrder) error {
	var err error
	switch p.DropType {
	case "before", "after": //拖拽到某个节点之前||拖拽到某个节点之后
		//使用事务闭包
		err = global.GvaDb.Transaction(func(tx *gorm.DB) error {
			var newOrderNum uint
			if p.DropType == "before" { //插到前面取被插队的排序数字作为排序，，后续累加一保持顺序
				newOrderNum = p.DropNode.OrderNum
			} else if p.DropType == "after" { //插到后面取插队排序数字加一作为排序，后续累加一保持顺序
				newOrderNum = p.DropNode.OrderNum + 1
			}
			//事先查出菜单列表
			sameLevelMenuList, err := m.GetMenuChildList(p.DropNode.ParentId)
			if err != nil {
				return err
			}
			//拖拽节点更改排序数字及父级分类
			tx.Model(&sysModel.SysMenu{}).Where("id = ? ", p.DraggingNode.ID).Updates(sysModel.SysMenu{ParentId: p.DropNode.ParentId, OrderNum: newOrderNum})
			//调整之后节点的排序数字,之后的节点各加2，为插入节点留空隙
			isStartSet := false
			for _, menu := range sameLevelMenuList {
				if menu.ID == p.DropNode.ID { //从放置节点开始后续排序自动加一
					isStartSet = true
					if p.DropType == "after" { //插入到后面，则被放置节点不处理
						continue
					}
					if p.DropType == "before" { //插入到前面，从被放置节点开始排序开始调整

					}
				}
				if isStartSet && menu.ID != p.DraggingNode.ID {
					newOrderNum++
					tx.Model(&sysModel.SysMenu{}).Where("id = ? ", menu.ID).Update("order_num", newOrderNum)
				}
			}
			return nil
		})
	case "inner": //拖进节点，直接更改父级ID
		err = global.GvaDb.Model(&sysModel.SysMenu{}).Where("id = ? ", p.DraggingNode.ID).Update("parent_id", p.DropNode.ID).Error
	default:
		err = errors.New("无效的节点类型")
	}
	return err
}

//根据菜单KEY获取菜单分组
func (m *MenuService) GetMenuGroupByKey(groupKey string) (*sysModel.SysMenuGroup, error) {
	menuGroup := &sysModel.SysMenuGroup{}
	err := global.GvaDb.Where(&sysModel.SysMenuGroup{GroupKey: groupKey}).First(menuGroup).Error
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, errors.New("分组不存在")
		}
		return nil, err
	}
	return menuGroup, nil
}

//通过搜索参数获取分组列表
func (m *MenuService) GetMenuGroupList(p *sysRequest.SysMenuGroupList) ([]*sysModel.SysMenuGroup, int64, error) {
	var total int64
	var list []*sysModel.SysMenuGroup
	tx := global.GvaDb.Model(&sysModel.SysMenuGroup{})
	if p.GroupName != "" {
		tx = tx.Where("group_name like ?", "%"+p.GroupName+"%")
	}
	if p.GroupKey != "" {
		tx = tx.Where("group_key like ?", "%"+p.GroupKey+"%")
	}
	rs := tx.Count(&total).Order("id ASC").Find(&list)
	return list, total, rs.Error
}

//保存分组数据
func (m *MenuService) SaveMenuGroup(d *sysModel.SysMenuGroup) (int64, error) {
	var rs *gorm.DB
	if d.ID != 0 {
		rs = global.GvaDb.Where("id = ?", d.ID).Updates(d)
	} else {
		rs = global.GvaDb.Create(d)
	}
	return rs.RowsAffected, rs.Error
}

//删除分组
func (m *MenuService) DelMenuGroup(ids *comRequest.IdsReq) (int64, error) {
	rs := global.GvaDb.Unscoped().Delete(&sysModel.SysMenuGroup{}, ids.Ids)
	return rs.RowsAffected, rs.Error
}
